-- Matías Blanco & Sergio Pérez
-- Monos pasando por el puente. Práctica 1 - Semaforos en ADA

with Ada.Text_IO; use Ada.Text_IO;
with Semaforos;use Semaforos;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
with Ada.Numerics.Float_Random ; use Ada.Numerics.Float_Random;

procedure MonkeySemaforosAda is
   -- Costantes y variables
   MAXMONOS : constant integer := 50;
   MAXMONOSCRUZAR : constant integer := 5; -- Maximos de monos que pueden cruzar, de seguido, el puente, si superan ese numero tienen que dejar pasar a los otros. Los que estan cluzando, cuando se llega a ese numero, se deja que finalicen.
   G : Generator;
   type estadoPuente is (MonosEste,MonosOeste,Vacio);


   mutex,colaMonosEste,colaMonosOeste: Binary_Semaphore;
   NMonosPuente: integer:=0;
   pasadosMonosEste,pasadosMonosOeste: integer:=0;
   esperanMonosEste,esperanMonosOeste: integer:=0;
   PasanSeg: integer:=0;

   estado:estadoPuente:=Vacio;
   rand:float:=0.0;


   -- Definimos las tareas
   task type MonoOeste(id:integer);
   type PMonoOeste is access MonoOeste;
   MOeste: PMonoOeste;

   task type MonoEste(id:integer);
   type PMonoEste is access MonoEste;
   MEste:PMonoEste;

   task body MonoOeste is
   begin
      Wait(mutex);
      Put("-- El mono ");Put(id,0);Put(" del Oeste ");Put(" quiere cruzar el puente.");New_Line;
      -- Si estan cruzando monos del Oeste
      if(estado=MonosOeste) then
         -- Si ya no pueden pasar mas monos del Oeste
         if(esperanMonosOeste>0 or pasadosMonosOeste=MAXMONOSCRUZAR or NMonosPuente=4) then
            esperanMonosOeste:=esperanMonosOeste+1;
            Signal(mutex);
            Wait(colaMonosOeste);
         else
            -- Si hay monos del Este esperando. Tenemos en cuenta los monos que estan pasando del Oeste
            if(esperanMonosEste>0) then
               pasadosMonosOeste:=pasadosMonosOeste+1;
            end if;
            NMonosPuente:=NMonosPuente+1;
            Signal(mutex);
         end if;
      else
         -- Si estan pasando monos del Este, tenemos que esperar
         if(estado=MonosEste) then
            esperanMonosOeste:=esperanMonosOeste+1;
            Signal(mutex);
            Wait(colaMonosOeste);
         else
            -- Si esta vacio podemos empezar a cruzar
            if(estado=Vacio) then
               estado:=MonosOeste;
               pasadosMonosOeste:=0;
               NMonosPuente:=NMonosPuente+1;
               Signal(mutex);
            end if;
         end if;
      end if;

      Put("--> El mono ");Put(id,0);Put(" del Oeste ");Put(" esta cruzando el puente.");New_Line;
      delay 3.0;
      Put("<-- El mono ");Put(id,0);Put(" del Oeste ");Put(" ha cruzando el puente.");New_Line;

      Wait(mutex);
      -- Si hay monos esperando del Este
      if(esperanMonosEste>0) then
         -- Si podemos segir cruzando
         if(esperanMonosOeste>0 and pasadosMonosOeste<MAXMONOSCRUZAR) then
            pasadosMonosOeste:=pasadosMonosOeste+1;
            Signal(colaMonosOeste);
            Signal(mutex);
         else
            -- Si era el ultimo, dejamos pasar a los del Este
            if NMonosPuente=1 then
               NMonosPuente:=0;
               estado:=MonosEste;
               -- Si habia mas de 4 monos esperando del Este solo dejamos pasar 4
               if(esperanMonosEste>4) then
                  PasanSeg:=4;
               else
                  PasanSeg:=esperanMonosEste;
               end if;
               for i in 1..PasanSeg loop
                  esperanMonosEste:=esperanMonosEste-1;
                  Signal(colaMonosEste);
               end loop;
               NMonosPuente:=PasanSeg;
               Signal(mutex);
            else
               NMonosPuente:=NMonosPuente-1;
               Signal(mutex);
            end if;
         end if;
      else
         -- Si hay mas monos del Oeste metemos a uno
         if(esperanMonosOeste>0) then
            esperanMonosOeste:=esperanMonosOeste-1;
            Signal(colaMonosOeste);
            Signal(mutex);
         else
            -- sino vaciamos el puente
            NMonosPuente:=NMonosPuente-1;
            if(NMonosPuente=0) then
               estado:=Vacio;
            end if;
            Signal(mutex);
         end if;
      end if;
   end MonoOeste;

   task body MonoEste is
   begin
      Wait(mutex);
      Put("-- El mono ");Put(id,0);Put(" del Este ");Put(" quiere cruzar el puente.");New_Line;

      if(estado=MonosEste) then
         if(esperanMonosEste>0 or pasadosMonosEste=MAXMONOSCRUZAR or NMonosPuente=4) then
            esperanMonosEste:=esperanMonosEste+1;
            Signal(mutex);
            Wait(colaMonosEste);
         else
            if(esperanMonosOeste>0) then
               pasadosMonosEste:=pasadosMonosEste+1;
            end if;
            NMonosPuente:=NMonosPuente+1;
            Signal(mutex);
         end if;
      else
         if(estado=MonosOeste) then
            esperanMonosEste:=esperanMonosEste+1;
            Signal(mutex);
            Wait(colaMonosEste);
         else
            if(estado=Vacio) then
               estado:=MonosEste;
               pasadosMonosEste:=0;
               NMonosPuente:=NMonosPuente+1;
               Signal(mutex);
            end if;
         end if;
      end if;

      Put("--> El mono ");Put(id,0);Put(" del Este ");Put(" esta cruzando el puente.");New_Line;
      delay 3.0;
      Put("<-- El mono ");Put(id,0);Put(" del Este ");Put(" ha cruzando el puente.");New_Line;

      Wait(mutex);
      if(esperanMonosOeste>0) then
         if(esperanMonosEste>0 and pasadosMonosEste<MAXMONOSCRUZAR) then
            pasadosMonosEste:=pasadosMonosEste+1;
            Signal(colaMonosEste);
            Signal(mutex);
         else
            if NMonosPuente=1 then
               NMonosPuente:=0;
               estado:=MonosOeste;
               if(esperanMonosOeste>4) then
                  PasanSeg:=4;
               else
                  PasanSeg:=esperanMonosOeste;
               end if;
               for i in 1..PasanSeg loop
                  esperanMonosOeste:=esperanMonosOeste-1;
                  Signal(colaMonosOeste);
               end loop;
               NMonosPuente:=PasanSeg;
               Signal(mutex);
            else
               NMonosPuente:=NMonosPuente-1;
               Signal(mutex);
            end if;
         end if;
      else
         if(esperanMonosEste>0) then
            esperanMonosEste:=esperanMonosEste-1;
            Signal(colaMonosEste);
            Signal(mutex);
         else
            NMonosPuente:=NMonosPuente-1;
            if(NMonosPuente=0) then
               estado:=Vacio;
            end if;
            Signal(mutex);
         end if;
      end if;
   end MonoEste;

begin
   mutex:=Init(1);
   colaMonosEste:=Init(0);
   colaMonosOeste:=Init(0);

   Put("---- COMIENZAN A LLEGAR MONOS");New_Line;New_Line;
   for i in 1..MAXMONOS loop
      Reset(G);
      rand:=Random(G);

      if(rand>0.5) then
         MEste:=new MonoEste(i);
      else
         MOeste:=new MonoOeste(i);
      end if;
      delay(0.5);
   end loop;
end MonkeySemaforosAda;
